<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
<html>
<head>
<title>Custom widget</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="Custom widget, Visual Basic, GTK#, tutorial, programming, Linux">
<meta name="description" content="Custom widget">
<meta name="language" content="en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>

<body>

<div class="container">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<div class="content">


<a href="/" title="Home">Home</a>&nbsp;
<a href="..">Contents</a>


<h1>Custom widget</h1>


<p>
Have you ever looked at an application and wondered, how a particular gui item was created?
Probably every wannabe programmer has. Then you were looking at a list of widgets provided 
by your favourite gui library. But you couldn't find it. Toolkits usually provide only the 
most common widgets like buttons, text widgets, sliders etc. 
No toolkit can provide all possible widgets.
</p>

<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div> 

<p>
There are actually two kinds of toolkits. Spartan toolkits and heavy weight toolkits. 
The FLTK toolkit is a kind of a spartan toolkit. It provides only the very basic widgets and assumes, 
that the programemer will create the more complicated ones himself. wxWidgets is a heavy weight one.
It has lots of widgets. Yet it does not provide the more specialized widgets. 
For example a speed meter widget, a widget that measures the capacity of a CD to be 
burned (found e.g. in nero). Toolkits also don't have usually charts.
</p>

<p>
Programmers must create such widgets by themselves. They do it by using the drawing tools 
provided by the toolkit. 
There are two possibilities. A programmer can modify or enhance an existing widget.
Or he can create a custom widget from scratch. 
</p>

<h2>Burning widget</h2>

<p>
This is an example of a widget, that we create from scratch. This widget can be found in various media
burning applications, like Nero Burning ROM.
</p>

<div class="codehead">custom.vb</div>
<pre class="code">
Imports Gtk

NameSpace BurningWidget

Public Class Burning 
    Inherits DrawingArea 

    Const PANEL_HEIGHT As Integer = 30
    Const DIVISIONS As Integer = 10
    Const FULL_CAPACITY As Double = 700
    Const MAX_CAPACITY As Double = 750
    
    Dim redColor As New Gdk.Color(1, 0.7, 0.7)
    Dim yellowColor As New Gdk.Color(1, 1, 0.7)

    Dim parent As Widget 
    
    Dim num() As String = { _
        "75", "150", "225", "300", _
        "375", "450", "525", "600", _
        "675" _
    }


    Public Sub New(ByVal parent As Widget) 
        
        Me.SetSizeRequest(1, PANEL_HEIGHT)

        Me.parent = parent
        AddHandler Me.ExposeEvent, AddressOf Me.OnExpose
      
    End Sub


    Private Sub OnExpose(ByVal sender As Object, ByVal e As ExposeEventArgs) 

        Dim cc As Cairo.Context = Gdk.CairoHelper.Create(sender.GdkWindow)

        Me.DrawCustomWidget(cc)
        
        Dim disposeTarget As IDisposable = CType(cc.Target, IDisposable)
        disposeTarget.Dispose
        
        Dim disposeContext As IDisposable = CType(cc, IDisposable)
        disposeContext.Dispose

    End Sub
    
    Private Sub DrawCustomWidget(ByVal cc As Cairo.Context)
    
        cc.LineWidth = 0.8
        
        cc.SelectFontFace("Courier 10 Pitch", _
            Cairo.FontSlant.Normal, Cairo.FontWeight.Normal)
        cc.SetFontSize(11)
        
        Dim burn As Custom.GtkVBApp = CType(parent, Custom.GtkVBApp)

        Dim slid_width As Double = burn.GetCurrentWidth
        Dim width As Double = Allocation.Width
        Dim move As Double = width / DIVISIONS

        Dim till As Double = (width / MAX_CAPACITY) * slid_width
        Dim full As Double = (width / MAX_CAPACITY) * FULL_CAPACITY


        If slid_width >= FULL_CAPACITY
            
            cc.SetSourceRGB(1.0, 1.0, 0.72)
            cc.Rectangle(0, 0, full, PANEL_HEIGHT)
            cc.Clip
            cc.Paint
            cc.ResetClip
            
            cc.SetSourceRGB(1.0, 0.68, 0.68)
            cc.Rectangle(full, 0, till-full, PANEL_HEIGHT)  
            cc.Clip
            cc.Paint
            cc.ResetClip

        Else 
             
            cc.SetSourceRGB(1.0, 1.0, 0.72)
            cc.Rectangle(0, 0, till, PANEL_HEIGHT)
            cc.Clip
            cc.Paint
            cc.ResetClip
       End If

       cc.SetSourceRGB(0.35, 0.31, 0.24)
       
       For i As Integer = 1 To num.Length
           
           cc.MoveTo(i*move, 0)
           cc.LineTo(i*move, 5)    
           cc.Stroke
           
           Dim extents As Cairo.TextExtents = cc.TextExtents(num(i-1))
           cc.MoveTo(i*move-extents.Width/2, 15)
           cc.TextPath(num(i-1))
           cc.Stroke
       Next   
    
    End Sub

End Class

End Namespace
</pre>

<p>
We put a <b class="keyword">DrawingArea</b> on the bottom of the window and draw the entire widget manually. 
All the important code resides in the <b class="keyword">DrawCustomWidget</b> which is called from the
<b class="keyword">OnExpose</b> method of the Burning class. 
This widget shows graphically the total capacity of a medium and the free space available to us. 
The widget is controlled by a scale widget. The minimum value of our custom widget is 0, the maximum is 750. 
If we reach value 700, we began drawing in red colour. This normally indicates overburning.  
</p>

<pre class="explanation">
 Dim num() As String = { _
     "75", "150", "225", "300", _
     "375", "450", "525", "600", _
     "675" _
 }
</pre>

<p>
These numbers are shown on the burning widget. They show the capacity of the medium.
</p>


<pre class="explanation">
 Dim burn As Custom.GtkVBApp = CType(parent, Custom.GtkVBApp)

 Dim slid_width As Double = burn.GetCurrentWidth
</pre>

<p>
These two lines get the current number from the scale widget. 
We get the parent widget and from the parent widget, we get the current value. 
</p>

<pre class="explanation">
Dim till As Double = (width / MAX_CAPACITY) * slid_width
Dim full As Double = (width / MAX_CAPACITY) * FULL_CAPACITY
</pre>

<p>
We use the <b class="keyword">width</b> variable to do the transformations. Between 
the values of the scale and the custom widget's measures. Note that we use floating
point values. We get greater precision in drawing.
The <b class="keyword">till</b> parameter determines the total size to be drawn. 
This value comes from the slider widget. It is a proportion of the whole area. 
The <b class="keyword">full</b> parameter determines the point,
where we begin to draw in red color. 
</p>


<pre class="explanation">
 cc.SetSourceRGB(1.0, 1.0, 0.72)
 cc.Rectangle(0, 0, till, PANEL_HEIGHT)
 cc.Clip
 cc.Paint
 cc.ResetClip
</pre>

<p>
This code here, draws a yellow rectangle up to point, where the medium is full.
</p>

<pre class="explanation">
 Dim extents As Cairo.TextExtents = cc.TextExtents(num(i-1))
 cc.MoveTo(i*move-extents.Width/2, 15)
 cc.TextPath(num(i-1))
 cc.Stroke
</pre>

<p>
This code here draws the numbers on the burning widget. We calculate the
<b class="keyword">TextExtents</b> to position the text correctly.
</p>

<div class="codehead">burning.vb</div>
<pre class="code">
' ZetCode Mono Visual Basic GTK# tutorial
'
' In this program, we create
' a custom widget
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk
 
NameSpace Custom

Public Class GtkVBApp
    Inherits Window
 
    Const MAX_CAPACITY As Integer = 750

    Dim cur_value As Integer
    Dim burning As BurningWidget.Burning
 
    Public Sub New
        
        MyBase.New("Burning")
        
        Me.InitUI
        
        Me.SetDefaultSize(350, 200)
        
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll
        
    End Sub
    
    Private Sub InitUI
        
        Dim vbox As New VBox(False, 2)
        
        Dim scale As New HScale(0, MAX_CAPACITY, 1)
        scale.SetSizeRequest(160, 35)
        
        AddHandler scale.ValueChanged, AddressOf Me.OnChanged
        
        Dim fixed As New Fixed
        fixed.Put(scale, 50, 50)
        
        vbox.PackStart(fixed)
        
        burning = New BurningWidget.Burning(Me)
        vbox.PackStart(burning, False, False, 0)

        Me.Add(vbox)

    End Sub
    
    Private Sub OnChanged(ByVal sender As Object, ByVal args As EventArgs)
        cur_value = sender.Value
        burning.QueueDraw
    End Sub

    
    Public Function GetCurrentWidth As Integer
        Return cur_value
    End Function
    
    Sub OnDelete(ByVal sender As Object, ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main
    
        Application.Init
        Dim app As New GtkVBApp
        Application.Run
        
    End Sub
 
End Class

End Namespace
</pre>

<p>
This is the main class. 
</p>

<pre class="explanation">
 Private Sub OnChanged(ByVal sender As Object, ByVal args As EventArgs)
     cur_value = sender.Value
     burning.QueueDraw
 End Sub
</pre>

<p>
We get the value from the scale widget, store it in the <b class="keyword">cur_value</b> variable 
for later use. We redraw the burning widget. 
</p>

<br>
<img src="/img/gui/vbgtk/burning.png" alt="Burning widget">
<div class="figure">Figure: Burning widget</div>

<hr class="btm">

<p>
In this chapter, we created a custom widget in GTK# and Visual Basic.
</p>

<br>
<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div> 
<br>

<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified May 25, 2009  <span class="copyright">&copy; 2007 - 2012 Jan Bodnar</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>

